#pragma once

#include <iostream>
#include <string>
#include <memory>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "Log.hpp"
#include "InetAddr.hpp"

enum
{
    OK,
    CREATE_ERR,
    BIND_ERR,
    LISTEN_ERR
};

// 设计模式: 模板方法类
// 套接字模板
class Socket
{
    // 交给子类实现的方法
public:
    virtual ~Socket() {}
    virtual void CreatSocketOrDie() = 0;
    virtual void BindSocketOrDie(int port) = 0;
    virtual void LisenSocketOrDie(int backlog) = 0;
    virtual std::shared_ptr<Socket> Accept(InetAddr *addr) = 0; // 参数是输出型参数, 要把值带出来
    virtual int GetSocket() = 0;
    virtual void Close() = 0;
    virtual ssize_t Recv(std::string *out) = 0;
    virtual ssize_t Send(std::string &in) = 0;
    virtual bool Connect(InetAddr &addr) = 0;

public:
    void BuildLisentSocketMethod(int port, int backlog = 8)
    {
        CreatSocketOrDie();
        BindSocketOrDie(port);
        LisenSocketOrDie(backlog);
    }
    void BuildClientSocketMethod()
    {
        CreatSocketOrDie();
    }
};

class TcpSocket : public Socket
{
public:
    TcpSocket() : _sockfd(-1)
    {
    }
    TcpSocket(int sockfd) : _sockfd(sockfd)
    {
    }
    void CreatSocketOrDie() override
    {
        _sockfd = socket(AF_INET, SOCK_STREAM, 0);
        if (_sockfd < 0)
        {
            LOG(LogLevel::FATAL) << "create socket error";
            exit(CREATE_ERR);
        }
        LOG(LogLevel::INFO) << "create socket success";
    }
    void BindSocketOrDie(int port) override
    {
        InetAddr local(port);
        if (bind(_sockfd, local.Addr(), local.Length()) != 0)
        {
            LOG(LogLevel::FATAL) << "bind socket error";
            exit(BIND_ERR);
        }
        LOG(LogLevel::INFO) << "bind socket success";
    }
    void LisenSocketOrDie(int backlog) override
    {
        if (listen(_sockfd, backlog) != 0)
        {
            LOG(LogLevel::FATAL) << "listen socket error";
            exit(LISTEN_ERR);
        }
        LOG(LogLevel::INFO) << "listen socket success";
    }
    std::shared_ptr<Socket> Accept(InetAddr *addr) override
    {
        struct sockaddr_in peer;
        socklen_t len = sizeof(peer);
        int fd = accept(_sockfd, (struct sockaddr *)&peer, &len);
        if (fd < 0)
        {
            LOG(LogLevel::WARING) << "accept socket error";
            return nullptr;
        }
        LOG(LogLevel::INFO) << "Accept socket success";
        addr->Init(peer);
        return std::make_shared<TcpSocket>(fd);
    }

    bool Connect(InetAddr &addr) override
    {
        int n = connect(_sockfd, addr.Addr(), addr.Length());
        if (n >= 0)
        {
            return true;
        }
        return false;
    }

    // 重点: 读操作
    ssize_t Recv(std::string *out) override
    {
        char buffer[1024];
        ssize_t n = recv(_sockfd, buffer, sizeof(buffer) - 1, 0);
        if (n > 0) // 读取成功
        {
            buffer[n] = 0;
            // 重点: 拼接式的读取传输过来的报文, 根据报文长度来判断是否写入, 不完整的话就继续读取
            // 判断报文是否完整的第一步
            *out += buffer; // out中的内容是不断增多的
        }
        return n;
    }

    // 写操作
    ssize_t Send(std::string &in) override
    {
        return send(_sockfd, in.c_str(), in.size(), 0);
    }

    int GetSocket()
    {
        return _sockfd;
    }

    void Close() override
    {
        if (_sockfd > 0)
        {
            close(_sockfd);
        }
    }

    ~TcpSocket() {}

private:
    int _sockfd;
};